<?php
/**
 * Created by PhpStorm.
 * User: Eric-pc3
 * Date: 2018/5/17
 * Time: 18:21
 */

namespace app\common\middleware;

use app\common\api\ApiHelper;
use app\common\logic\ApiLogic;
use PalePurple\RateLimit\RateLimit;
use think\facade\Log;
use think\facade\Request;

class OpenApi
{
    public $token = '';
    private $version = 'v1';
    private $allowVersion = [ // 允许的版本
    ];

    private $account = ''; //token 验证正确后返回
    private $userId = 0;//token 验证正确后返回

    private $path = '';
    private $param = [];

    /**
     * 使用中间件 接管 路由 不用再写 控制器了
     * @param $request
     * @param \Closure $next
     * @return mixed|\think\response\Json
     * @throws \Exception
     */
    public function handle($request, \Closure $next)
    {
        $controllerName = $request->controller();
        $actionName = $request->action();

        // 跨域
        $this->allowCrossDomain();

        $this->getAllowVersion();
        $this->getVersion();

//        Log::debug('API - 开始处理');
        // 如果 没有注解 为 API 返回没有地址
        if(!getControllerActionAnnotationsIsApi($this->version,$controllerName,$actionName)){
            return json(ApiHelper::output(3));
        }
        $this->path = $controllerName . '/' . $actionName;

        $this->param = Request::param();
        $this->param['version'] = $this->version;
        $ip = Request::ip();
        $this->param['ip'] = $ip;

        // 限流 防止 频繁请求
        // 60 秒内 允许请求 120 次
        $rateLimit = new RateLimit("api_all", 120, 60, getRateLimitAdapter());
        if (!$rateLimit->check($ip)) {
            return ApiHelper::output(1,'您的操作太频繁了');
        }

        // 读取注解 是否 不检测 token 是不检测的反 是检测
        if(!getControllerActionAnnotationsIsNoCheckToken($this->version,$controllerName,$actionName)){
            // 获取 token
            $this->getToken();
            if(empty($this->token)){
                exceptionBusiness('token不能为空',1000);
            }

            $this->param['token'] = $this->token;

            // 校验 token
            $this->checkToken();
            // 校验 token 后 才有 account 和 user_id
            $this->param['account'] = $this->getAccount();
            $this->param['user_id'] = $this->getUserId();
        }

        $apiLogic = new ApiLogic();
        // 检测 IP
        if (!$apiLogic->checkIpRules($ip, '/api/' . $this->path, $this->version)) {
            $jieguo = ApiHelper::output(-3);
        }
        else{
            try {
                // 执行 获取结果
                $jieguo = action($this->path,['param' => $this->param],$this->version);
            }
            catch (\app\common\exception\BusinessException $bs){
                // 业务错误，某参数不能为空等
                $jieguo = ApiHelper::output($bs->getCode(),$bs->getMessage());
            }
            catch (\app\common\exception\LogicException $ls){
                // 逻辑错误
                $jieguo = ApiHelper::output($ls->getCode(),$ls->getMessage());
            }
            catch (\Exception $exception) {
                Log::error('OpenApi action msg: ' . $exception->getMessage());
                $jieguo = ApiHelper::output(-2);
            }
        }

        // 写日志
        $this->addRequestLog($ip,json_encode($jieguo,256));
        return json($jieguo);
    }

    /**
     * 获取token 从 参数或者 头部
     */
    private function getToken()
    {
        $this->token = input('token');
        if(empty($this->token)){
            $this->token = Request::header('token');
        }
    }

    /**
     * 从 env 获取允许的API版本
     */
    private function getAllowVersion()
    {
        $this->allowVersion = explode(',',env('API_ALLOW_VERSION'));
    }

    /**
     * 获取接口版本
     */
    private function getVersion()
    {
        $v = input('v');
        if(empty($v)){
            $v = Request::header('v');
        }
        // 在允许列表里面才进行赋值
        if(in_array($v,$this->allowVersion)){
            $this->version = $v;
        }
    }

    /**
     * 检测 token
     * @throws \Exception
     * @throws \think\Exception
     * @throws \think\exception\DbException
     */
    private function checkToken()
    {
        $apiLogic = new ApiLogic();
        $jieguo = $apiLogic->checkToken($this->token,$this->version);

        if($jieguo['err_code'] !==  0){
            exceptionBusiness($jieguo['msg'],$jieguo['err_code']);
        }

        $this->account = $jieguo['data']['mobile'];
        $this->userId = $jieguo['data']['user_id'];
    }

    /**
     * 验证token后获取账号
     * @return string
     */
    private function getAccount(){
        return $this->account;
    }

    /**
     * 验证token后获取用户ID
     * @return int
     */
    private function getUserId(){
        return $this->userId;
    }

    /**
     * 写入请求日志
     * @param $return
     * @param string $remark
     */
    private function addRequestLog($ip,$return,$remark = ''){
        $apilogic = new ApiLogic();
        $apilogic->addRequestLog([
            'user_id' => $this->getUserId(),
            'token' => $this->token,
            'url' => '/api/' . $this->path,
            'version' => $this->version,
            'param' => json_encode($this->param,256),
            'return' => $return,
            'ip' => $ip,
            'remark' => $remark,
        ]);
    }

    /**
     *  输出 允许 跨域 头
     */
    private function allowCrossDomain()
    {
        header('Access-Control-Allow-Origin: *');
//        header('Access-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-Requested-With');
        header('Access-Control-Allow-Headers: *');
        header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE');
        header('Access-Control-Max-Age: 1728000');
    }
}